package holdem;

import java.util.*;

/**
 *
 * @author Pai Qi
 * @author qipai0@gmail.com
 * Description: A skeleton class of card comparator
 * Start Date:
 *
 */

public class CardComparator {
    public static Integer[] compare(Card tableCard[], Card hand[][]) {

        // get 7-card list per player
        ArrayList<ArrayList<Card>> cards = new ArrayList<ArrayList<Card>>();
        for(int i = 0; i < hand.length; i++) {
            // taking care of null cells
            if(hand[i] == null) {
                continue;
            }

            ArrayList<Card> c = new ArrayList<Card>();
            c.addAll(Arrays.asList(tableCard));
            c.addAll(Arrays.asList(hand[i]));
            cards.add(c);
        }

        List<List<Card>> bestCombine = new ArrayList<List<Card>>();
        // get best possible 5-card list for all players
        for(int i = 0; i < hand.length; i++) {
            // get best possible 5-card list for each player
            // choose 5 from 7
            List<Card> best = null;
            List<Card> last = new ArrayList<Card>();
            for(int exclude1 = 0; exclude1 < cards.get(0).size()-1; exclude1++) {
                for(int exclude2 = exclude1+1; exclude2 < cards.get(0).size(); exclude2++) {
                    // create a five-card list
                    
                    
                    for(int k = 0; k < cards.get(0).size(); k++) {
                        if(k != exclude1 && k != exclude2) {
                            last.add(cards.get(i).get(k));
                        }
                    }
                    // linear-time compare
                    if(best == null) {
                        best = last;
                        
                    } else {
                        best = compareFive(best, last) < 0 ? last : best;
                    }
                    last = new ArrayList<Card>();
                }
            }
            bestCombine.add(best);
        }

        // compare between players (linear
        List<Integer> interResult = new ArrayList<Integer>();

        for(Integer i = 0; i < cards.size(); i++) {
            if(interResult.isEmpty()) {
            interResult.add(0);
                continue;
            }
            int bestIndex = interResult.get(0);
            int compareBestWithLast =
                    compareFive(bestCombine.get(bestIndex), bestCombine.get(i));
            if( compareBestWithLast > 0) {

            } else if( compareBestWithLast == 0) {
                interResult.add(i);
            } else if( compareBestWithLast < 0) {
                interResult.clear();
                interResult.add(i);
            }
        }

        return interResult.toArray(new Integer[]{});
    }

    private static Integer compareFive(List<Card> hand1, List<Card> hand2) {
        assert hand1.size() == 5 && hand2.size() == 5 : "Can only compare 5 cards";
        HandTypes hand1type = new Hand(hand1).BestHand();
        HandTypes hand2type = new Hand(hand2).BestHand();
        int compareResult = hand1type.compareTo(hand2type);
        // not in same hand type: easy
        if(compareResult != 0) {
            return compareResult;
        }
        // in same hand type: hard
        if(hand1type == HandTypes.RFLUSH) {
            return 0;
        }

        sortCardsZA(hand1);
        sortCardsZA(hand2);

        if(hand1type == HandTypes.STRIATFLUSH) {

            return hand1.get(1).compareTo(hand2.get(1));
        }

//        int[] hand1count = valueCount(hand1);
//        int[] hand2count = valueCount(hand2);

        if(hand1type == HandTypes.FOURKIND) {
            return compareRecursively(hand1, hand2, new int[]{4,1} , 0);
        }

        if(hand1type == HandTypes.FULLHOUSE) {
            return compareRecursively(hand1, hand2, new int[]{3,2} , 0);
        }

        if(hand1type == HandTypes.FLUSH) {
            return compareRecursively(hand1, hand2, new int[]{1,1,1,1,1} , 0);
        }

        if(hand1type == HandTypes.STRAIT) {
            return hand1.get(1).compareTo(hand2.get(1));
        }
        if(hand1type == HandTypes.THREEKIND) {
            return compareRecursively(hand1, hand2, new int[]{3,1,1} , 0);
        }
        if(hand1type == HandTypes.TWOPAIR) {
            return compareRecursively(hand1, hand2, new int[]{2,2,1} , 0);
        }
        if(hand1type == HandTypes.PAIR) {
            return compareRecursively(hand1, hand2, new int[]{2,1,1,1} , 0);
        }
        if(hand1type == HandTypes.HIGHCARD) {
            return compareRecursively(hand1, hand2, new int[]{1,1,1,1,1} , 0);
        }
        return null;
    }

    private static int compareRecursively(List<Card> hand1, List<Card> hand2, int[] cardCounts, int level) {
        int[] hand1count = valueCount(hand1);
        int[] hand2count = valueCount(hand2);

        int wait1 = 0;
        int wait2 = 0;
        // check prev. card count
        if(level != 0) {
            for(int i = 0; i < level; i++) {
                if(cardCounts[i] == cardCounts[level]) {
                    wait1++;
                    wait2++;
                }
            }
            
        }

        for(int i = 13; i >= 0; i--) {

            int hand1c = hand1count[i];
            int hand2c = hand2count[i];

            // reset and wait for next pattern
            if(hand1c != 0 && wait1 != 0) {
                wait1--;
                hand1c = 0;
            }
            if(hand2c != 0 && wait2 != 0) {
                wait2--;
                hand2c = 0;
            }
            

            if(hand1c==cardCounts[level] && hand2c==cardCounts[level]) {
                if(level == cardCounts.length - 1) {
                    return 0;
                }
                return compareRecursively(hand1, hand2, cardCounts, level+1);
                
            }
            if(hand1c==cardCounts[level]) {
                return 1;
            }
            if(hand2c==cardCounts[level]) {
                return -1;
            }
        }
        return 0;


    }

    private static void sortCardsZA(List<Card> cards) {
        for(int i = 0; i < cards.size()-1; i++) {
            for(int j = 0; j < cards.size()-i-1; j++) {
                if(cards.get(j).compareTo(cards.get(j+1)) < 0) {
                    cards.add(j, cards.remove(j+1));
                }
            }
        }
    }

    private static int[] valueCount(List<Card> cards) {
        int[] count = new int[14];
        for(Card c : cards) {
            count[c.getValue()-1]++;
            if(c.getValue() == 1) {
                count[13]++;
            }
        }
        return count;
    }
    
}
